﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace BarcodePrint
{
    public class PrintWithDriver
    {
        /// <summary>
        /// 打印机名称
        /// </summary>
        public String PrinterName { get; set; }

        public PrintWithDriver(string printerName)
        {
            this.PrinterName = printerName;
        }

        public bool Print(string cmd)
        {
            byte[] bytes = Encoding.Default.GetBytes(cmd);
            return this.PrintByDriver(bytes);
        }

        public bool Print(byte[] bytes)
        {
            return this.PrintByDriver(bytes);
        }

        private bool PrintByDriver(byte[] bytes)
        {
            bool result = false;
            try
            {
                if (!string.IsNullOrEmpty(PrinterName))
                {
                    result = WindowsDriverPrint.SendBytesToPrinter(PrinterName, bytes);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
            return result;
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class PrintDocInfo
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string DocName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string OutputFile;
        [MarshalAs(UnmanagedType.LPStr)]
        public string DataType;
    }

    internal static class WindowsDriverPrint
    {
        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string printerName, out IntPtr intptrPrinter, IntPtr intptrPrintDocument);

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool ClosePrinter(IntPtr intptrPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartDocPrinter(IntPtr intptrPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] PrintDocInfo docInfo);

        [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndDocPrinter(IntPtr intptrPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartPagePrinter(IntPtr intptrPrinter);

        [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndPagePrinter(IntPtr intptrPrinter);

        [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool WritePrinter(IntPtr intptrPrinter, IntPtr intptrBytes, Int32 count, out Int32 written);

        #region 定义发送原始数据到打印机的方法

        private static bool SendBytesToPrinter(string printerName, IntPtr intptrBytes, Int32 count)
        {
            Int32 error = 0, written = 0;
            IntPtr intptrPrinter = new IntPtr(0);
            PrintDocInfo docInfo = new PrintDocInfo();
            bool bSuccess = false;

            docInfo.DocName = ".NET RAW Document";
            docInfo.DataType = "RAW";

            // Open the printer.
            if (OpenPrinter(printerName.Normalize(), out intptrPrinter, IntPtr.Zero))
            {
                // Start a document.
                if (StartDocPrinter(intptrPrinter, 1, docInfo))
                {
                    // Start a page.
                    if (StartPagePrinter(intptrPrinter))
                    {
                        // Write your bytes.
                        bSuccess = WritePrinter(intptrPrinter, intptrBytes, count, out written);
                        EndPagePrinter(intptrPrinter);
                    }
                    EndDocPrinter(intptrPrinter);
                }
                ClosePrinter(intptrPrinter);
            }
            // If you did not succeed, GetLastError may give more information
            // about why not.
            if (bSuccess == false)
            {
                error = Marshal.GetLastWin32Error();
            }
            return bSuccess;
        }

        public static bool SendFileToPrinter(string printerName, string fileName)
        {
            // Open the file.
            FileStream fs = new FileStream(fileName, FileMode.Open);
            // Create a BinaryReader on the file.
            BinaryReader br = new BinaryReader(fs);
            // Dim an array of bytes big enough to hold the file's contents.
            Byte[] bytes = new Byte[fs.Length];
            bool bSuccess = false;
            // Your unmanaged pointer.
            IntPtr pUnmanagedBytes = new IntPtr(0);
            int nLength;

            nLength = Convert.ToInt32(fs.Length);
            // Read the contents of the file into the array.
            bytes = br.ReadBytes(nLength);
            // Allocate some unmanaged memory for those bytes.
            pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
            // Copy the managed byte array into the unmanaged array.
            Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
            // Send the unmanaged bytes to the printer.
            bSuccess = SendBytesToPrinter(printerName, pUnmanagedBytes, nLength);
            // Free the unmanaged memory that you allocated earlier.
            Marshal.FreeCoTaskMem(pUnmanagedBytes);
            return bSuccess;
        }

        public static bool SendBytesToPrinter(string printerName, byte[] bytes)
        {
            bool bSuccess = false;
            IntPtr pUnmanagedBytes = new IntPtr(0);
            int nLength = bytes.Length;
            // Allocate some unmanaged memory for those bytes.
            pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
            // Copy the managed byte array into the unmanaged array.
            Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
            // Send the unmanaged bytes to the printer.
            bSuccess = SendBytesToPrinter(printerName, pUnmanagedBytes, nLength);
            // Free the unmanaged memory that you allocated earlier.
            Marshal.FreeCoTaskMem(pUnmanagedBytes);
            return bSuccess;
        }

        public static bool SendStringToPrinter(string printerName, string text)
        {
            IntPtr pBytes;
            Int32 dwCount;
            // How many characters are in the string?
            dwCount = (text.Length + 1) * Marshal.SystemMaxDBCSCharSize;
            // Assume that the printer is expecting ANSI text, and then convert
            // the string to ANSI text.
            pBytes = Marshal.StringToCoTaskMemAnsi(text);
            // Send the converted ANSI string to the printer.
            SendBytesToPrinter(printerName, pBytes, dwCount);
            Marshal.FreeCoTaskMem(pBytes);
            return true;
        }

        #endregion
    }
}
